#include <stdio.h>
#include <stdlib.h>
#include <error.h>
#include <errno.h>
#include <assert.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <math.h>
#include <sys/wait.h>
#include <sys/select.h>
#include <sys/time.h>

#include "utility.h"
#include "rsa.h"
#include "format.h"
#include "net.h"

static struct User{
    char m_name[64];
    char m_passwd[64];
    u_int32_t   m_position;
    int64_t     m_timestamp;
    u_int8_t    m_isStudy;
}user ={
    .m_name = "dy",
    .m_passwd = "psddy",
    .m_position = 0,
    .m_timestamp = 1,
    .m_isStudy = 0,
};

int sockfd = -1;//套接口

int client_login(int sockfd);
int connect_serv(const char const *ip,short port);
void socket_read(int sockfd, void *arg);
int delClientRecv(int sockfd,u_int16_t cmd,u_int32_t packet_len,char *buf);
int saveTimestamp(void * buf,u_int32_t packet_len);
void *monitor_sockfd_stdin(void *arg);
void signal_alarm(int sig);
void stdin_read();


int main(int argc,char *argv[])
{
    if(argc<3){
        printf("./%s [ip] [port]\n",argv[0]);
        exit(0);
    }
    /* 1.与服务器建立连接 */
    sockfd = connect_serv(argv[1],atoi(argv[2]));
    assert(sockfd != -1);

    /* 2.先进行模拟登陆 */
    client_login(sockfd);

    /* 3.创建一个线程循环监听标准输入与套接字 */
    pthread_t tid;
    if(pthread_create(&(tid), NULL,monitor_sockfd_stdin, NULL) != 0){
        perror("create threads error");
    }

    /* 4.定时发送心跳 */
        //很奇怪，定时的话检测不到标准输入
    // signal(SIGALRM,signal_alarm);
    // alarm(5);
    while(1){
        sleep(5);
        signal_alarm(SIGALRM);
    }
}
void signal_alarm(int sig)
{
    if(sig !=SIGALRM){
        return ;
    }
    if(user.m_isStudy){
        user.m_timestamp ++;
    }else{
        user.m_timestamp --;
    }

    /*发送世间戳*/
    char send_cmd[128];
    int len = encodeTimestamp(send_cmd+6,user.m_position,user.m_timestamp);
    head_package(send_cmd,e_msgTimestamp,len);
    write(sockfd,send_cmd,Msg.m_msgLen+len);

    //alarm(5);//设置5s后再次触发
    printf("I am going to send heartbeat packet\n");
}
/**
 * @brief  监听套接字与标准输入
 * @note   
 * @param  *arg: 
 * @retval None
 */
void *monitor_sockfd_stdin(void *arg)
{
    fd_set  rfds;
    int retval;
    while(1){
        FD_ZERO(&rfds);
        FD_SET(sockfd,&rfds);
        FD_SET(0,&rfds);        //监听标准输入
        retval = select(sockfd+1,&rfds,NULL,NULL,NULL);
        printf("retval = %d\n",retval);
        if(retval == -1){
            perror("select()");   
            printf("sockfd = %d\n",sockfd);
            exit(-1);
        }else if(retval){
            if(FD_ISSET(sockfd,&rfds)){
                socket_read(sockfd,NULL);
            }else if(FD_ISSET(0,&rfds)){
                stdin_read();    
            }else{
                printf("没有监听其他IO呀!??\n");
            }
        }else{
            printf("没有设置超时时间，没理由到这里啊!\n");
            exit(-1);
        }
    }
    return NULL;
}

/**
 * @brief  
 * @note   
 * @param  sockfd: 
 * @retval 
 */
int client_login(int sockfd)
{
    char buf[MAX_MESSAGE_SIZE];
    char name_passwd[MAX_MESSAGE_SIZE];

    /* 1.按格式填写姓名密码字段 */
    int len = snprintf(name_passwd,MAX_MESSAGE_SIZE,"$%s;%s;pen;\n",user.m_name,user.m_passwd);
    len = len+key.m_public.m_bytes-len%key.m_public.m_bytes;

    /* 2.对登陆信息进行加密 */
    int *encode = encodeMessage(len, key.m_public.m_bytes, name_passwd,
            key.m_public.m_exponent, key.m_public.m_modulus);
    int encode_len = len/key.m_public.m_bytes*sizeof(int);

    /* 3.构造数据包 */
    memcpy(buf+Msg.m_msgLen, encode, encode_len);
    head_package(buf,e_msgLogin,encode_len);
    write(sockfd,buf,Msg.m_msgLen+encode_len);

    free(encode);
    return 0;
}

/** 
 * @brief  创建套接字并与服务器建立TCP连接
 * @note   
 * @param  *ip: 
 * @param  port: 
 * @retval 成功返回套接字描述符.失败返回-1
 */
int connect_serv(const char const *ip,short port)
{
    int sockfd = socket(AF_INET,SOCK_STREAM,0);
    assert(sockfd != -1);

    struct sockaddr_in clientAddr;
    socklen_t addrlen = sizeof(clientAddr);
    bzero(&clientAddr,addrlen);
    clientAddr.sin_family=AF_INET;
    clientAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    clientAddr.sin_port = 0;
    int err = bind(sockfd,(struct sockaddr*)&clientAddr,addrlen);//不绑定也行的
    assert(err != -1);

    clientAddr.sin_addr.s_addr = inet_addr(ip);
    clientAddr.sin_port = htons(port);
    err = connect(sockfd,(struct sockaddr*)&clientAddr,addrlen);
    if(err == -1) {
        printf("ip:%s,port:%d\n",ip,port);
        perror("connect server");
        exit(0);
    }

    return sockfd;
}

/**
 * @brief  读套接字
 * @note   
 * @param  sockfd: 
 * @param  *arg: 
 * @retval None
 */
void socket_read(int sockfd, void *arg)
{
    printf("*****************\n");
    char buf[MAX_MESSAGE_SIZE];
    bzero(buf,MAX_MESSAGE_SIZE);
    int count=0;

    /* 1.对端发送FIN后，还向这个套接字写会收到 RST */
    if ( (count = read(sockfd, buf, Msg.m_msgLen )) < 0) {
        perror("read sockfd");
        close(sockfd);  //关掉这个套接字
        sockfd = -1;
        return ;
    }
    /* 2.对方发送了FIN,服务器读会返回0，应答后处于CLOSE_WAIT状态 */
    else if (count == 0){
        printf("\ra user closed  \n");
        close(sockfd);  //关掉这个套接字
        sockfd = -1;
        return ;
    }
    /* 3.没有读到6个字节 */
    else if(count < 6){
        printf("messages is less than 6\n");
        close(sockfd);  //关掉这个套接字
        sockfd = -1;
        return ;
    }

    /* 4.正常读数据 */
    unsigned short cmd_num = 0;
    unsigned int packet_len = 0;
    head_analyze(buf,&cmd_num,&packet_len);
    count = read(sockfd, buf, packet_len);
    if(count < packet_len){
        printf("cmd_num = %d,count = %d\n,packet_len = %d\n",cmd_num,count,packet_len);
        printf("read failed!??\n");
        return ;
    }
    //处理加密后的时间戳
    delClientRecv(sockfd,cmd_num,packet_len,buf);
    return ;
}
void stdin_read()
{
    printf("........................\n");
    char buf[MAX_MESSAGE_SIZE];
    fgets(buf,MAX_MESSAGE_SIZE,stdin);
    if(strncmp(buf, "start", strlen("start")) == 0){
        user.m_isStudy = 1;
        printf("start stydy\n");
    }else if(strncmp(buf, "stop", strlen("stop")) == 0){
        user.m_isStudy = 0;
        printf("stop stydy\n");
    }else{
        printf("unknow command!\n");
    }
}
int delClientRecv(int sockfd,u_int16_t cmd,u_int32_t packet_len,char *buf)
{
    signed int err = 0;
    //char send_cmd[MAX_MESSAGE_SIZE];
    //printf("cmd num = %d\n",cmd);
    switch (cmd){
        case e_msgDebug:
            printf("recv: %s",buf);
            break;
        case e_msgTimestamp:
             //保存时间戳
             //printf("receive timestamp\n");
             err = saveTimestamp(buf,packet_len);
             break;
        default:
            printf("unknow cmd\n");
            return -1;
    }
    if(err){
        // head_package(send_cmd,err,0);
        // bufferevent_write(bev, send_cmd, Msg.m_msgLen);
        printf("error ??\n");
    }
    return 0;
}
/**
 * @brief  保存时间戳
 * @note   为了保证加密后的数据每次都不一样，所以把时间戳放在前面，position放在后面
 * @param  buf: 
 * @param  packet_len: 
 * @retval 
 */
int saveTimestamp(void * buf,u_int32_t packet_len)
{
    printf("packet_len = %d\n",packet_len);
    decodeTimestamp(buf,packet_len,&user.m_position,&user.m_timestamp);
    printf("position:%d,time:%s\n",user.m_position,ctime((time_t *)&user.m_timestamp));
    return 0;
}
